package com.smartwebx.ha.biz.rainwater.real.impl;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Maps.EntryTransformer;
import com.google.common.collect.Multimaps;
import com.smartwebx.ha.biz.rainwater.real.LayerRainfallManager;
import com.smartwebx.ha.biz.rainwater.real.ProductManager;
import com.smartwebx.ha.biz.rainwater.real.RainfallManager;
import com.smartwebx.ha.dal.assign.bean.HaArea;
import com.smartwebx.ha.dal.assign.dao.HaStationAreaMapper;
import com.smartwebx.ha.dal.realtime.dao.RainfallMapper;
import com.smartwebx.ha.dal.realtime.dao.StationMapper;
import com.smartwebx.ha.dal.usebean.DateValue;
import com.smartwebx.ha.dal.usebean.DateValueTotal;
import com.smartwebx.ha.dal.usebean.GisBase;
import com.smartwebx.ha.dal.usebean.GisRain;
import com.smartwebx.ha.dal.usebean.GisRainLayerRainfall;
import com.smartwebx.ha.dal.usebean.SimpleStatGroup2;
import com.smartwebx.ha.dal.usebean.SplitDatePointBean;
import com.smartwebx.ha.dal.usebean.StationValue;
import com.smartwebx.ha.dal.usebean.SuperStatGroup;
import com.smartwebx.ha.dal.usebean.SuperStatGroup2;
import com.smartwebx.ha.dal.util.DateFullProcess;
import com.smartwebx.ha.dal.util.DatePoint;
import com.smartwebx.ha.dal.util.DateSection;
import com.smartwebx.ha.dal.util.DateValueHandler;
import com.smartwebx.ha.dal.util.MyFunctions;
import com.smartwebx.ha.dal.util.SplitDatePoint;
import com.smartwebx.ha.util.GisRainStationInProvOrInCity;

@Service
public class RainfallManagerImpl implements RainfallManager {
	@Autowired
	private StationMapper stationDao;
	@Autowired
	private RainfallMapper rainfallDao;
	@Autowired
	private LayerRainfallManager layerRainfallManager;
	@Autowired
	private HaStationAreaMapper haStationAreaDao;
	@Autowired
	private ProductManager productManager;

	private Collection<String> queryStats;

	@Override
	public GisRainLayerRainfall getGisRainLayerRainfall(Date bTm, Date eTm,
			String type, HaArea area) throws IOException {
		// VisoLine visoline = new VisoLine();
		// Map<String, Double> statValMap = getStartEndRainfall(bTm, eTm);
		String startTm = "";
		String endTm = "";
		Calendar cal = Calendar.getInstance();
		int nowHour = cal.get(Calendar.HOUR_OF_DAY);
		Date now = new Date();
		now = DateUtils.truncate(now, Calendar.HOUR);

		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

		if (Objects.equal("yestDay", type)) {// 昨
			if (nowHour >= 8) {
				Date t1 = DateUtils.addDays(now, -1);
				t1 = DateUtils.setHours(t1, 8);
				Date t2 = DateUtils.setHours(now, 7);

				startTm = sdf.format(t1);
				endTm = sdf.format(t2);
			} else if (nowHour < 8) {
				Date t1 = DateUtils.addDays(now, -2);
				t1 = DateUtils.setHours(t1, 8);

				Date t2 = DateUtils.addDays(now, -1);
				t2 = DateUtils.setHours(t2, 7);

				startTm = sdf.format(t1);
				endTm = sdf.format(t2);
			}
		} else if (Objects.equal("nowDay", type)) {// 今
			if (nowHour >= 8) {
				Date t1 = DateUtils.setHours(now, 8);
				Date t2 = now;

				startTm = sdf.format(t1);
				endTm = sdf.format(t2);
			} else if (nowHour < 8) {
				Date t1 = DateUtils.addDays(now, -1);
				t1 = DateUtils.setHours(t1, 8);
				Date t2 = now;

				startTm = sdf.format(t1);
				endTm = sdf.format(t2);
			}
		} else if (Objects.equal("freeDate", type)) {// 自定义
			startTm = sdf.format(bTm);
			endTm = sdf.format(eTm);
		}
		// Map<String, Double> statValMap =
		// CallOracle.getRainDataByCall(startTm,
		// endTm);
		// System.out.println(statValMap.size());
		//
		// List<Entry<String, Double>> statValList =
		// Lists.newArrayList(statValMap
		// .entrySet());
		// Ordering<Entry<String, Double>> ordering = new Ordering<Entry<String,
		// Double>>() {
		// @Override
		// public int compare(Entry<String, Double> e1,
		// Entry<String, Double> e2) {
		// int res = e2.getValue().compareTo(e1.getValue());
		// return res;
		// }
		// };
		// Collections.sort(statValList, ordering);

		List<StationValue> statVals = productManager.findStatVals(startTm,
				endTm);
		Map<String, Double> statValMap = keyToValuesMap(statVals,
				GetStationFunction.INSTANCE, GetValFunction.INSTANCE);

		Map<String, GisBase> statGisMap = stationDao.getAllStatInfo();
		List<GisRain> gisRainDatas = Lists.newArrayList();
		List<GisRain> rainKmlDatas = Lists.newArrayList();
		GisRainStationInProvOrInCity gisRainStationInProvOrInCity = new GisRainStationInProvOrInCity();

		Integer gradeLegend1 = 0, gradeLegend2 = 0, gradeLegend3 = 0, gradeLegend4 = 0, gradeLegend5 = 0, gradeLegend6 = 0;
		StringBuffer strBuffer1 = new StringBuffer();
		StringBuffer strBuffer2 = new StringBuffer();
		strBuffer2.append("[");
		for (StationValue stationVal : statVals) {
			Double val = stationVal.getVal();
			String station = stationVal.getStcd();

			if (!statGisMap.containsKey(station))
				continue;
			GisBase gb = statGisMap.get(station);
			GisRain rain = new GisRain(gb);
			rain.setVal(val);
			// 判断站点是不是小水库
			if (rain.getStcd().indexOf("B") != -1) {
				rain.setSmallReservoir(true);
			} else {
				rain.setSmallReservoir(false);
			}
			if (val == null) {
				continue;
			}
			try {
				if (val > 0 && val < 10.0) {
					rain.setGradeLegend("gradeLegend1");
					gradeLegend1++;
				} else if (val >= 10.0 && val < 25) {
					rain.setGradeLegend("gradeLegend2");
					gradeLegend2++;
				} else if (val >= 25 && val < 50) {
					rain.setGradeLegend("gradeLegend3");
					gradeLegend3++;
				} else if (val >= 50 && val < 100) {
					rain.setGradeLegend("gradeLegend4");
					gradeLegend4++;
				} else if (val >= 100 && val < 250) {
					rain.setGradeLegend("gradeLegend5");
					gradeLegend5++;
				} else if (val >= 249.9) {
					rain.setGradeLegend("gradeLegend6");
					gradeLegend6++;
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			rain.setInProvOrInCity(gisRainStationInProvOrInCity.inProvOrInCity(
					rain, area));
			gisRainDatas.add(rain);
			rainKmlDatas.add(rain);
			strBuffer1.append(rain.getLgtd()).append(",");
			strBuffer1.append(rain.getLttd()).append(",");
			strBuffer1.append(rain.getVal()).append(",");
			strBuffer1.append(rain.getStnm()).append(",");
			strBuffer1.append(rain.getStcd() + "|");
			// 面雨量
			strBuffer2.append("[\"");
			strBuffer2.append(rain.getLgtd()).append("\",");
			strBuffer2.append("\"");
			strBuffer2.append(rain.getLttd()).append("\",");
			strBuffer2.append("\"");
			strBuffer2.append(rain.getVal().toString()).append("\",");
			strBuffer2.append("\"");
			strBuffer2.append(rain.getStcd()).append(rain.getVal())
					.append("\"],");
		}
		strBuffer2.append("]");
		String rainData = "";
		if (strBuffer1.length() > 0) {
			rainData = strBuffer1.substring(0, strBuffer1.length() - 1)
					.toString();
		}
		// System.out.println(rainData);

		// Gson gson = new Gson();
		// // 面雨量
		// String[][] src = gson.fromJson(strBuffer2.toString(),
		// String[][].class);
		// String areaBln = area.getName().substring(0, area.getName().length()
		// - 1) + "边界.bln";
		/*
		 * visoline.drawit(visoline.getsrcPath("visoline",src),
		 * "title","autoSet"," ", "visoline",areaBln);
		 */
		List<SuperStatGroup> superSimpleStatGs = layerRainfallManager
				.getLayerRainfall(area, statValMap);

		List<SuperStatGroup2> simpleStatGroup = layerRainfallManager.getSimpleStatGroup(area.getLayerRainCode(),
				startTm, endTm);

		GisRainLayerRainfall gisRainLayerRain = new GisRainLayerRainfall();
		gisRainLayerRain.setGisRains(gisRainDatas);
		gisRainLayerRain.setLayerRainfalls(simpleStatGroup);
		gisRainLayerRain.setGradeLegend1(gradeLegend1);
		gisRainLayerRain.setGradeLegend2(gradeLegend2);
		gisRainLayerRain.setGradeLegend3(gradeLegend3);
		gisRainLayerRain.setGradeLegend4(gradeLegend4);
		gisRainLayerRain.setGradeLegend5(gradeLegend5);
		gisRainLayerRain.setGradeLegend6(gradeLegend6);
		gisRainLayerRain.setDataStr(rainKmlDatas);
		gisRainLayerRain.setAllStationStr(rainData);
		return gisRainLayerRain;
	}

	private List<GisRain> getAllStatRainVal(Date startDate, Date endDate) {
		Map<String, Double> statValMap = getStartEndRainfall(startDate, endDate);
		Map<String, GisBase> statGisMap = stationDao
				.getStatBaseInfoInQuery(queryStats);

		List<GisRain> gisRainDatas = Lists.newArrayList();

		for (String station : statValMap.keySet()) {
			Double val = statValMap.get(station);
			if (!statGisMap.containsKey(station) || val == 0d)
				continue;
			GisBase gb = statGisMap.get(station);
			GisRain rain = new GisRain(gb);
			rain.setVal(val);

			gisRainDatas.add(rain);
		}

		return gisRainDatas;
	}

	private Map<String, Double> getStartEndRainfall(Date startDate, Date endDate) {
		SplitDatePointBean splitDatePointBean = SplitDatePoint.splitDatePoint(
				startDate, endDate);
		String operatType = splitDatePointBean.getOperatType();
		Date startTime = splitDatePointBean.getStartDate();
		Date endTime = splitDatePointBean.getEndDate();
		Date startHour = splitDatePointBean.getStartHour();
		Date endHour = splitDatePointBean.getEndHour();

		Map<String, Double> statVal = null;

		if ("127".indexOf(operatType) > -1) {// 计算小时累计雨量
			List<StationValue> statVals = rainfallDao
					.getAllStatHourValStcdFilter(startHour, endHour);

			statVal = keyToValuesMap(statVals, GetStationFunction.INSTANCE,
					GetValFunction.INSTANCE);
		} else if (operatType.equals("3")) {// 计算单日雨量
			// 统一加一天
			startTime = DateUtils.addDays(startTime, 1);
			endTime = DateUtils.addDays(endTime, 1);
			List<StationValue> statVals = rainfallDao
					.getAllStatDayValStcdFilter(startTime, endTime);

			statVal = keyToValuesMap(statVals, GetStationFunction.INSTANCE,
					GetValFunction.INSTANCE);
		} else if ("45".indexOf(operatType) > -1) {// 计算旬日雨量与小时累计雨量
			statVal = this
					.getAllStatVal(startTime, endTime, startHour, endHour);

		} else if (operatType.equals("6")) {// 计算旬日雨量
			List<StationValue> statVals = this.getAllStatDayVal(startTime,
					endTime);
			statVal = keyToValuesMap(statVals, GetStationFunction.INSTANCE,
					GetValFunction.INSTANCE);
		}

		queryStats = statVal.keySet();

		return statVal;
	}

	@Override
	public List<DateValue> getSingleStatDayProcess(String stcd, Date startDate,
			Date endDate) {
		List<DateValue> result = Lists.newArrayList();
		Map<Date, Double> dayValMap = Maps.newLinkedHashMap();
		SplitDatePointBean splitDatePoint = SplitDatePoint.splitDatePoint(
				startDate, endDate);

		String operatType = splitDatePoint.getOperatType();
		Date startTime = splitDatePoint.getStartDate();
		Date endTime = splitDatePoint.getEndDate();
		Date startHour = splitDatePoint.getStartHour();
		Date endHour = splitDatePoint.getEndHour();

		Date startNextTime = null;
		Date endNextTime = null;
		Date nowDate = DateUtils.truncate(new Date(), Calendar.DATE);

		if ("127".indexOf(operatType) <= -1) {
			startNextTime = DateUtils.addDays(startTime, 1);
			endNextTime = DateUtils.addDays(endTime, 1);
		}

		if ("127".indexOf(operatType) > -1) {
			// 小时雨量总值
			Double rainVal = rainfallDao.getNowDateRainVal(stcd, startHour,
					endHour);

			if (Objects.equal(operatType, "2")) {
				// 因为未超过8时，所以日期为当前日期前一天
				Date afterDate = DateUtils.addDays(nowDate, -1);
				dayValMap.put(afterDate, rainVal);
			} else {
				// 日期为当前日期
				dayValMap.put(nowDate, rainVal);
			}
		} else if (Objects.equal(operatType, "3")) {
			// 单日日雨量值 下一日8时雨量值
			List<DateValue> rainProcess = rainfallDao.getSingleStatOneDayVal(
					stcd, startNextTime);

			// 判断有无数据，如果没有则为填 0
			if (rainProcess.isEmpty()) {
				dayValMap.put(startTime, 0d);
			} else {
				dayValMap.put(startTime,
						Iterables.getFirst(rainProcess, new DateValue())
								.getVal());
			}

		} else if ("45".indexOf(operatType) > -1) {
			// 多日日雨量与当日各小时段雨量
			List<DateValue> moreRainProcess = null;
			if (Objects.equal(operatType, "5")) {
				moreRainProcess = rainfallDao.getSingleStatMoreDay(stcd,
						startNextTime, endTime);
			} else if (Objects.equal(operatType, "4")) {
				moreRainProcess = rainfallDao.getSingleStatMoreDay(stcd,
						startNextTime, endNextTime);
			}
			Double rainVal = rainfallDao.getNowDateRainVal(stcd, startHour,
					endHour);
			Map<Date, Double> timeValMap = DateFullProcess.returnDateSectStr(
					startTime, endNextTime);

			for (DateValue dv : moreRainProcess) {
				Date tm = dv.getTm();
				tm = DateUtils.truncate(tm, Calendar.DATE);
				tm = DateUtils.addDays(tm, -1);

				if (timeValMap.containsKey(tm)) {
					timeValMap.put(tm, dv.getVal());
				}
			}

			if (Objects.equal(operatType, "5")) {
				Date afterDate = DateUtils.addDays(nowDate, -1);
				timeValMap.put(afterDate, rainVal);
			} else if (Objects.equal(operatType, "4")) {
				timeValMap.put(nowDate, rainVal);
			}

			dayValMap = timeValMap;

		} else if (Objects.equal(operatType, "6")) {
			List<DateValue> moreRainProcess = rainfallDao.getSingleStatMoreDay(
					stcd, startNextTime, endNextTime);
			Map<Date, Double> timeValMap = DateFullProcess.returnDateSectStr(
					startTime, endTime);

			for (DateValue dv : moreRainProcess) {
				Date tm = dv.getTm();
				tm = DateUtils.truncate(tm, Calendar.DATE);
				tm = DateUtils.addDays(tm, -1);

				if (timeValMap.containsKey(tm)) {
					timeValMap.put(tm, dv.getVal());
				}
			}
			dayValMap = timeValMap;
		}

		for (Date tm : dayValMap.keySet()) {
			DateValue dv = new DateValue(tm, dayValMap.get(tm));
			result.add(dv);
		}
		return result;
	}

	@Override
	public List<DateValue> getSingleStatHourProcess(String stcd, Date startH,
			Date endH) {
		List<DateValue> hourRainProcess = Lists.newArrayList();
		SplitDatePointBean splitDatePoint = SplitDatePoint.splitDatePoint(
				startH, endH);

		String operatType = splitDatePoint.getOperatType();
		Date startTime = splitDatePoint.getStartDate();
		Date endTime = splitDatePoint.getEndDate();
		Date startHour = splitDatePoint.getStartHour();
		Date endHour = splitDatePoint.getEndHour();

		Date startHPoint = null, endHPoint = null;

		if ("127".indexOf(operatType) > -1) {
			startHPoint = startHour;
			endHPoint = endHour;
		} else if ("36".indexOf(operatType) > -1) {
			startHPoint = DateUtils.addHours(startTime, 9);

			endHPoint = DateUtils.addDays(endTime, 1);
			endHPoint = DateUtils.addHours(endHPoint, 8);
		} else if ("45".indexOf(operatType) > -1) {
			startHPoint = DateUtils.addHours(startTime, 9);
			endHPoint = endHour;
		}
		List<DateValue> hourRainProcessDb = rainfallDao.getSingleStatMoreHour(
				stcd, startHPoint, endHPoint);
		Map<Date, Double> hourValMap = DateFullProcess.returnTimeSectStr(
				startHPoint, endHPoint);

		for (final Date hourTime : hourValMap.keySet()) {
			Optional<DateValue> findOneOpt = Iterables.tryFind(
					hourRainProcessDb, new Predicate<DateValue>() {
						@Override
						public boolean apply(DateValue input) {
							Date tm = DateUtils.truncate(input.getTm(),
									Calendar.HOUR);
							return Objects.equal(tm, hourTime);
						}
					});
			Double val = 0d;
			if (findOneOpt.isPresent())
				val = Objects.firstNonNull(findOneOpt.get().getVal(), 0d);

			DateValue dv = new DateValue(hourTime, val);
			hourRainProcess.add(dv);
		}

		return hourRainProcess;
	}

	@Override
	public List<DateValueTotal> getSingleHourTotalRainProcess(String stcd,
			Date bTm, Date eTm) {
		List<DateValue> rainHourProcess = this.getSingleStatHourProcess(stcd,
				bTm, eTm);

		return Lists.transform(rainHourProcess,
				MyFunctions.transformDateValueTotal());
	}

	@Override
	public List<DateValueTotal> getSingleDayTotalRainProcess(String stcd,
			Date bTm, Date eTm) {
		List<DateValue> rainDayProcess = this.getSingleStatDayProcess(stcd,
				bTm, eTm);

		return Lists.transform(rainDayProcess,
				MyFunctions.transformDateValueTotal());
	}

	private Map<String, Double> getAllStatVal(Date startTime, Date endTime,
			Date startHour, Date endHour) {
		List<StationValue> dayStatVals = this.getAllStatDayVal(startTime,
				endTime);
		List<StationValue> hourStatVal = this.getAllStatHourVal(startHour,
				endHour);
		Iterables.addAll(dayStatVals, hourStatVal);

		return keyToValuesMap(dayStatVals, GetStationFunction.INSTANCE,
				GetValFunction.INSTANCE);
	}

	/**
	 * 获得各个站点小时的雨量值
	 * 
	 * @param startHour
	 * @param endHour
	 * @return
	 */
	private List<StationValue> getAllStatHourVal(Date startHour, Date endHour) {
		List<StationValue> result = rainfallDao.getAllStatHourValStcdFilter(
				startHour, endHour);
		return result;
	}

	/**
	 * 获得各个站点日旬月的雨量值
	 * 
	 * @param startT
	 * @param endT
	 * @return
	 */
	private List<StationValue> getAllStatDayVal(Date startT, Date endT) {
		List<DateSection> dateSects = new DatePoint().returnDatePoin(startT,
				endT);
		// 避免多线程异常
		ImmutableList<DateSection> dateSectsCopy = ImmutableList
				.copyOf(dateSects);
		List<StationValue> allStatVal = Lists.newArrayList();

		for (DateSection dsb : dateSectsCopy) {
			if (dsb.getType() == 0) { // 旬雨量
				Date date = dsb.getTenPointDate();
				List<StationValue> tenRainVal = rainfallDao
						.getDateSectionTenMonthValStcdFilter(date, 4);

				allStatVal.addAll(tenRainVal);
			} else if (dsb.getType() == 1) { // 日雨量
				Date bTMDate = dsb.getStartEndDate()[0];
				Date eTMDate = dsb.getStartEndDate()[1];

				// 统一加一天
				bTMDate = DateUtils.addDays(bTMDate, 1);
				eTMDate = DateUtils.addDays(eTMDate, 2);

				List<StationValue> dayRainVal = rainfallDao
						.getAllStatDayValStcdFilter(bTMDate, eTMDate);

				allStatVal.addAll(dayRainVal);
			} else if (dsb.getType() == 2) { // 月雨量
				Date date = dsb.getTenPointDate();
				List<StationValue> monthRainVal = rainfallDao
						.getDateSectionTenMonthValStcdFilter(date, 5);

				allStatVal.addAll(monthRainVal);
			}
		}

		// for (StationValue sv : allStatVal) {
		// if (sv.getStcd().equals("63126400")) {
		// System.out.println("12345");
		// }
		// }

		return allStatVal;
	}

	private static <E, K> Map<K, Double> keyToValuesMap(Iterable<E> elements,
			Function<E, K> keyFunction, Function<E, Double> valueFunction) {
		ImmutableListMultimap<K, E> keysToElements = Multimaps.index(elements,
				keyFunction);
		ListMultimap<K, Double> keysToValuesLazy = Multimaps.transformValues(
				keysToElements, valueFunction);
		ImmutableListMultimap<K, Double> resultMulitmap = ImmutableListMultimap
				.copyOf(keysToValuesLazy);

		EntryTransformer<K, Collection<Double>, Double> flagPrefixer = new EntryTransformer<K, Collection<Double>, Double>() {
			@Override
			public Double transformEntry(K key, Collection<Double> value) {
				return DateValueHandler.sum(value);
			}
		};

		Map<K, Double> result = Maps.transformEntries(resultMulitmap.asMap(),
				flagPrefixer);

		return result;
	}

	private enum GetValFunction implements Function<StationValue, Double> {
		INSTANCE;

		@Override
		public Double apply(StationValue value) {
			return Objects.firstNonNull(value.getVal(), 0d);
		}
	}

	private enum GetStationFunction implements Function<StationValue, String> {
		INSTANCE;

		@Override
		public String apply(StationValue station) {
			return station.getStcd();
		}
	}

	public static void main(String args[]) {
		List<StationValue> svs = Lists.newArrayList();

		StationValue v1 = new StationValue();
		v1.setStcd("123");
		v1.setVal(1d);
		svs.add(v1);
		v1 = new StationValue();
		v1.setStcd("123");
		v1.setVal(1d);
		svs.add(v1);
		v1 = new StationValue();
		v1.setStcd("123");
		v1.setVal(1d);
		svs.add(v1);
		v1 = new StationValue();
		v1.setStcd("123");
		v1.setVal(1d);
		svs.add(v1);

		v1 = new StationValue();
		v1.setStcd("321");
		v1.setVal(1.2d);
		svs.add(v1);

		Map<String, Double> result = keyToValuesMap(svs,
				GetStationFunction.INSTANCE, GetValFunction.INSTANCE);
		System.out.println(result);

		Date n = new Date();
		Date n2 = new Date();
		n = DateUtils.truncate(n, Calendar.DATE);
		n2 = DateUtils.truncate(n2, Calendar.DATE);
		ArrayList<Date> nList = Lists.newArrayList(n);

		System.out.println(nList.contains(n2));
	}

}
